// vue数据双向绑定：原理数据劫持

// 模拟vue中 data 选项
let data = {
  user_name: '张三',
  age: 24
}

// 模拟组件的实例对象
let _this = {}

// - 利用 Object.defineProperty() 进行数据劫持
//   + Object.defineProperty() 方法会直接在一个对象上定义一个新属性，或者修改一个对象的现有属性，并返回此对象。
// - 语法
//   + Object.defineProperty(obj, prop, descriptor)
// - 参数
//   + obj：要定义属性的对象。
//   + prop：要定义或修改的属性的名称或 Symbol 。
//   + descriptor：要定义或修改的属性描述符。
//   + 返回值：被传递给函数的对象。
// > 在ES6中，由于 Symbol类型的特殊性，用Symbol类型的值来做对象的key与常规的定义或修改不同，而Object.defineProperty【[详细说明](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)】 是定义key为Symbol的属性的方法之一。


for (let item in data) {
  // console.log(item, data[item]);
  Object.defineProperty(_this, item, {
    // get方法，用来获取扩展属性值的，当获取该属性值的时候调用 get() 方法
    get() {
      console.log('get()被调用了');
      return data[item]
    },
    set(newValue) {
      console.log('set()被调用了', newValue);
      // _this.user_name = newValue; // 千万不要在set方法中修改当前扩展属性的值，否则出现死循环
      data[item] = newValue
    }
  })
}

// > 直接修改不成，所以采用迂回战术。首先注意 我们 `_this` 身上的数据，是从 data 上数据劫持代理到 `_this` 身上的，所以要修改扩展属性(_this)的值取决于 data。所以 `data[item] = newValue`，下面修改的值，赋值给data；data属性值改变，当我们使用get方法，用来获取扩展属性值时，扩展属性(_this)的值相应随之改变。

console.log('扩展属性', _this);

// 修改属性值
// 通过 Object.defineproperty()的get方法添加的扩展属性不能直接进行 对象，属性 修改
_this.user_name = 'Jack'
console.log(_this.user_name)